package base.thread_demo.demo06;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 单生产者（交替生产）单消费者交替生产消费程序设计优化：将对资源的操作封装在资源对象内部。
 *
 * 多生产者消费者环境下保证不了交替生产问题（Input1.x没有同步导致）（t1生产完包子准备生产馒头，t3准备生产包子，）
 * while(flag)：解决虚假唤醒问题，notifyAll()解决存在唤醒同类型线程危险
 */
public class SingleProducterAndConsumer {
    public static void main(String[] args) {
        Resource1 r = new Resource1(false);//一开始认为没有资源，需要等待t1线程生产

        Input1 in = new Input1(r);
        Output1 out = new Output1(r);

        new Thread(in, "t1").start();
        new Thread(out, "t2").start();
//        new Thread(in, "t3").start();
//        new Thread(out, "t4").start();
    }
}

class Input1 implements Runnable {
    private Resource1 r;
    public Input1(Resource1 r) {
        this.r = r;
    }

    private int x=0;
    @Override
    public void run() {
       while (true) {
           if(x==0){
               try {
                   r.set("包子");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }else{
               try {
                   r.set("馒头");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           x = (x+1)%2;
       }
    }
}

class Output1 implements Runnable {
    private Resource1 r ;
    public Output1(Resource1 r){
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            try {
                r.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Resource1 {
    private static final Logger log = LoggerFactory.getLogger(Resource1.class);
    private String name;//资源名
    private boolean flag;//是否有资源产生

    public Resource1(boolean flag){
        this.flag = flag;
    }

    @Override
    public String toString() {
        return "Resource{" +
                "name='" + name + '\'' +
                ", flag=" + flag +
                '}';
    }

    /**
     * 根据flag判断是否要执行操作，且flag要同步操作的执行，故整个set()方法内容都需要同步
     */
    public synchronized void set(String name) throws InterruptedException {
        while(flag){// 如果flag=true，表示有资源，让当前线程释放锁，并进入线程池
            this.wait();
        }
        Thread.sleep(20);
        this.name = name;
        this.flag = !this.flag;
        log.info("------------> {} 生产了 {}", Thread.currentThread().getName(), this.name);
        this.notifyAll();
    }

    public synchronized void get() throws InterruptedException {
        while(!flag){// 如果flag=false，表示没有资源供输出，让当前线程释放锁，并进入线程池
            this.wait();
        }
        Thread.sleep(30);
        log.info("{} 消费掉 {}", Thread.currentThread().getName(), this.name);
        this.flag = !this.flag;
        this.notifyAll();
    }
}
